package image.noise.generators;

import image.processing.ImageProcessor;
import image.processing.ImageProcessor.CompressAlgorithm;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.List;

import random.generators.GaussianDistributionGenerator;
import random.generators.RandomUtils;

public class BlankAdditiveGaussianNoise extends AdditiveNoise {
	/*
	 * Duda que significa blanco?? esto soporta suma asecas
	 */
	public BlankAdditiveGaussianNoise(double mean, double squaredStdDeviation) {
		super.generator = new GaussianDistributionGenerator(mean, squaredStdDeviation);
	}

	public BufferedImage addNoise(BufferedImage img, CompressAlgorithm algorithm, int percentage) {
		BufferedImage ans;
		List<Double> randoms;
			
		if (percentage < 0 || percentage > 100) {
			throw new IllegalArgumentException("mal porcentaje");
		}
		
		if (img == null) {
			return null;
		} else {
			int por = (int) Math.floor((double) ((img.getHeight() * img.getWidth()) * percentage) / 100);
			
			randoms = generator.generateRandomNumbers(img.getHeight() * img.getWidth());
			
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {
				ans = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();

				boolean polution[][] = new boolean[img.getWidth()][img.getHeight()];
				
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						polution[x][y] = false;
					}
				}
				int polutionQty = por;

				while (polutionQty != 0) {
					int xrand = RandomUtils.generateRandomBetween(0,img.getWidth());
					int yrand = RandomUtils.generateRandomBetween(0,img.getHeight());
					
					if (!polution[xrand][yrand]) {
						polution[xrand][yrand] = true;
						polutionQty--;
					}
				}

				int intValues[][] = new int[img.getWidth()][img.getHeight()];
				int k = 0;
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						if (polution[x][y]) {
							int valor = img.getRaster().getSample(x, y, 0)
									+ (int) Math.floor(randoms.get(k));

							intValues[x][y] = valor;
							k++;
						}else{
							int valor = img.getRaster().getSample(x, y, 0);
							intValues[x][y] = valor;
						}
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues = ImageProcessor
							.DynamicRangeCompressionChannel(intValues);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues = ImageProcessor
							.LinearRangeCompressionChannel(intValues);
				}
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues[x][y]);
					}
				}
			} else {
				List<Double> randoms1 = generator.generateRandomNumbers(img
						.getHeight() * img.getWidth());
				List<Double> randoms2 = generator.generateRandomNumbers(img
						.getHeight() * img.getWidth());
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				/*
				 * Polution
				 */

				boolean polution0[][] = new boolean[img.getWidth()][img.getHeight()];
				boolean polution1[][] = new boolean[img.getWidth()][img.getHeight()];
				boolean polution2[][] = new boolean[img.getWidth()][img.getHeight()];

				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						polution0[x][y] = false;
						polution1[x][y] = false;
						polution2[x][y] = false;
					}
				}
				int polutionQty0 = por;
				int polutionQty1 = por;
				int polutionQty2 = por;

				// TODO No deberian ser independientes los while en cada canal? Si no
				// ni bien corta uno, el resto de los canales ya no se les agrega ruido
				// aunque no se si eso esta bien o mal, es solo una duda, miralo salta!
				while (polutionQty0 != 0 && polutionQty1 != 0 && polutionQty2 != 0) {
					int xrand0 = RandomUtils.generateRandomBetween(0,img.getWidth());
					int yrand0 = RandomUtils.generateRandomBetween(0,img.getHeight());
					int xrand1 = RandomUtils.generateRandomBetween(0,img.getWidth());
					int yrand1 = RandomUtils.generateRandomBetween(0,img.getHeight());
					int xrand2 = RandomUtils.generateRandomBetween(0,img.getWidth());
					int yrand2 = RandomUtils.generateRandomBetween(0,img.getHeight());
					
					if (!polution0[xrand0][yrand0] && polutionQty0 != 0) {
						polution0[xrand0][yrand0] = true;
						polutionQty0--;
					}
					
					if (!polution1[xrand1][yrand1] && polutionQty1 != 0) {
						polution1[xrand1][yrand1] = true;
						polutionQty1--;
					}
					
					if (!polution2[xrand2][yrand2] && polutionQty2 != 0) {
						polution1[xrand2][yrand2] = true;
						polutionQty2--;
					}
				}

				int intValues0[][] = new int[img.getWidth()][img.getHeight()];
				int intValues1[][] = new int[img.getWidth()][img.getHeight()];
				int intValues2[][] = new int[img.getWidth()][img.getHeight()];
				int i = 0, j = 0, k = 0;
				
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						if (polution0[x][y]) {
							intValues0[x][y] = (int) Math.floor(img.getRaster()
									.getSample(x, y, 0) + randoms.get(i));
						} else {
							intValues0[x][y] = img.getRaster()
									.getSample(x, y, 0);
						}
						if (polution1[x][y]) {
							intValues1[x][y] = (int) Math.floor(img.getRaster()
									.getSample(x, y, 1) + randoms1.get(j));
						}else{
							intValues1[x][y] = img.getRaster()
									.getSample(x, y, 1);
						}
						if (polution2[x][y]) {
							intValues2[x][y] = (int) Math.floor(img.getRaster()
									.getSample(x, y, 2) + randoms2.get(k));
						}else{
							intValues2[x][y] = img.getRaster()
									.getSample(x, y, 2);
						}
						i++;
						j++;
						k++;
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues0 = ImageProcessor
							.DynamicRangeCompressionChannel(intValues0);
					intValues1 = ImageProcessor
							.DynamicRangeCompressionChannel(intValues1);
					intValues2 = ImageProcessor
							.DynamicRangeCompressionChannel(intValues2);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues0 = ImageProcessor
							.LinearRangeCompressionChannel(intValues0);
					intValues1 = ImageProcessor
							.LinearRangeCompressionChannel(intValues1);
					intValues2 = ImageProcessor
							.LinearRangeCompressionChannel(intValues2);
				}
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues0[x][y]);
						raster.setSample(x, y, 1, intValues1[x][y]);
						raster.setSample(x, y, 2, intValues2[x][y]);
					}
				}
			}
			return ans;
		}
	}
}
